#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace cv;
using namespace std;

static Mat src;
static Mat src_gray;
static int thresh = 50;
static int max_thresh = 255;
static RNG rng(12345);

static void thresh_callback(int, void *);
static bool is_square(vector<Point> &contour);
static double angle(Point pt1, Point pt2, Point pt0);

int main_contours(int, char **argv)
{
    src = imread("..\\data\\square5.jpg");
    if (src.empty())
    {
        cerr << "No image supplied ..." << endl;
        return -1;
    }
    cvtColor(src, src_gray, COLOR_BGR2GRAY);
    //blur(src_gray, src_gray, Size(5, 5));
    const char *source_window = "Source";
    namedWindow(source_window, WINDOW_AUTOSIZE);
    imshow(source_window, src);
    createTrackbar(" Canny thresh:", "Source", &thresh, max_thresh, thresh_callback);
    thresh_callback(0, 0);
    waitKey(0);
    return(0);
}

static void thresh_callback(int, void *)
{
    Mat canny_output;
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    Canny(src_gray, canny_output, thresh, thresh * 2, 3);
    namedWindow("Canny", WINDOW_AUTOSIZE);
    imshow("Canny", canny_output);
    findContours(canny_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
    Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);
    for (size_t i = 0; i < contours.size(); i++)
    {
		if (is_square(contours[i]))
		{
			//Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
			Scalar color = Scalar(0, 0, 255);
			//drawContours(drawing, contours, (int)i, color, 2, 8, hierarchy, 0, Point());
			Rect rect = boundingRect(contours[i]);
			rectangle(drawing, rect, color);
		}
    }
    namedWindow("Contours", WINDOW_AUTOSIZE);
    imshow("Contours", drawing);
}

static bool is_square(vector<Point> &contour)
{
	vector<Point> approx;

	// approximate contour with accuracy proportional
	// to the contour perimeter
	approxPolyDP(Mat(contour), approx, arcLength(Mat(contour), true) * 0.02, true);

	// square contours should have 4 vertices after approximation
	// relatively large area (to filter out noisy contours)
	// and be convex.
	// Note: absolute value of an area is used because
	// area may be positive or negative - in accordance with the
	// contour orientation
	if (approx.size() == 4  &&
		fabs(contourArea(Mat(approx))) > 1000 &&
		isContourConvex(Mat(approx)))
	{
		return true;
		double maxCosine = 0;

		for (int j = 2; j < 5; j++)
		{
			// find the maximum cosine of the angle between joint edges
			double cosine = fabs(angle(approx[j % 4], approx[j - 2], approx[j - 1]));
			maxCosine = MAX(maxCosine, cosine);
		}

		// if cosines of all angles are small
		// (all angles are ~90 degree) then write quandrange
		// vertices to resultant sequence
		if (maxCosine < 0.3)
		{
			return true;
		}
	}

	return false;
}

// helper function:
// finds a cosine of angle between vectors
// from pt0->pt1 and from pt0->pt2
static double angle(Point pt1, Point pt2, Point pt0)
{
	double dx1 = pt1.x - pt0.x;
	double dy1 = pt1.y - pt0.y;
	double dx2 = pt2.x - pt0.x;
	double dy2 = pt2.y - pt0.y;
	return (dx1*dx2 + dy1*dy2) / sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}